/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.hdfs.server.namenode.bookkeeper.metadata;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ComparisonChain;
import org.apache.hadoop.hdfs.protocol.FSConstants;
import org.apache.hadoop.hdfs.server.namenode.bookkeeper.BookKeeperEditLogInputStream;

/**
 * Metadata for a journal stored in a BookKeeper ledger. Implements equals,
 * hashCode, and compareTo method (from Comparable interface) to allow
 * equality checking and natural order sorting (by comparing the first
 * transaction id).
 * </p>
 * Currently the metadata is stored inside a ZNode, but it could be stored
 * in any other media (e.g., in the path name of a ZNode, or in a database).
 * @see BookKeeperEditLogInputStream
 * @see BookKeeperEditLogInputStream
 */
public class EditLogLedgerMetadata implements Comparable<EditLogLedgerMetadata> {

  private final int logVersion;
  private final long ledgerId;
  private final long firstTxId;
  private final long lastTxId;

  /**
   * Creates a new metadata object.
   * @param logVersion The layout version of the edit log segment stored in
   *                   this ledger.
   * @param ledgerId  BookKeeper-specific id of the ledger.
   * @param firstTxId First HDFS transaction id stored in the segment.
   * @param lastTxId Last HDFS transaction id store in the segment or -1 if
   *                 the segment is in progress.
   */
  public EditLogLedgerMetadata(int logVersion,
      long ledgerId,
      long firstTxId,
      long lastTxId) {
    Preconditions.checkArgument(firstTxId >= 0);
    Preconditions.checkArgument(lastTxId >= -1);
    if (lastTxId > -1 && lastTxId < firstTxId) {
        throw new IllegalArgumentException("Ledger can not be empty!");
    }
    this.logVersion = logVersion;
    this.ledgerId = ledgerId;
    this.firstTxId = firstTxId;
    this.lastTxId = lastTxId;
  }

  /**
   * Creates a new EditLogLedgerMetadata instance that represents the current
   * instance that has been finalized with the specified last transaction id
   * @param lastTxId The last transaction id in the finalized ledger
   * @return Instance representing the finalized ledger
   */
  public EditLogLedgerMetadata finalizeWithLastTxId(long lastTxId) {
    return new EditLogLedgerMetadata(this.logVersion, this.ledgerId,
        this.firstTxId, lastTxId);
  }

  /**
   * Return layout version version of the edit log segment stored in this ledger.
   * Usually specified in {@link FSConstants} by
   * <code>FSConstants.LAYOUT_VERSION</code>
   */
  public int getLogVersion() {
    return logVersion;
  }

  /**
   * Returns BookKeeper specific id of the ledger. Ids are generated by
   * BookKeeper itself.
   */
  public long getLedgerId() {
    return ledgerId;
  }

  /**
   * Returns the first HDFS transaction id of the ledger.
   */
  public long getFirstTxId() {
    return firstTxId;
  }

  /**
   * Returns the last HDFS transaction id of the ledger if the ledger is
   * finalized or -1 if the ledger is in-progress.
   */
  public long getLastTxId() {
    return lastTxId;
  }

  /**
   * If this instance is identical to the specified instance, return 0.
   * If the instances are not identical, return -1 if this instance's first
   * transaction id is less than the other instance's first transaction id,
   * or 1 otherwise.
   */
  @Override
  public int compareTo(EditLogLedgerMetadata other) {
    if (this.equals(other)) {
      return 0;
    }
    return firstTxId < other.getFirstTxId() ? -1 : 1;
  }

  /**
   * Determine whether two instances are identical by checking whether
   * firstTxId, lastTxId, ledgerId, and logVersion are equal.
   * @param o The instance to compare to this
   * @return True if instances identical
   */
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || !(o instanceof EditLogLedgerMetadata)) {
      return false;
    }
    EditLogLedgerMetadata that = (EditLogLedgerMetadata) o;
    return firstTxId == that.firstTxId &&
        lastTxId == that.lastTxId &&
        ledgerId == that.ledgerId &&
        logVersion == that.logVersion;
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(logVersion, ledgerId, firstTxId, lastTxId);
  }

  @Override
  public String toString() {
    return "EditLogLedgerMetadata{" +
        "logVersion=" + logVersion +
        ", ledgerId=" + ledgerId +
        ", firstTxId=" + firstTxId +
        ", lastTxId=" + lastTxId +
        '}';
  }
}

